package threadDemo;
import org.apache.log4j.Logger;

import java.rmi.RemoteException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;

/**
 * @author Ajie
 * @date 2023/7/21
 * @function settingsmanager 超时。返回。
 *
 */
public class Future_CompletableFuture {
    private static final Logger logger = Logger.getLogger(Future_CompletableFuture.class);

    private static final String  RemoteExceptionTAG = "RemoteExceptionTAG";

    public static void main(String[] args) throws RemoteException{

        RemoteService remoteService = new RemoteService();
        logger.info("main: start");
        long start = System.currentTimeMillis();
        String abc = remoteService.timeoutGet("abc");
        logger.info("main: result:"+abc +"  耗时:"+(System.currentTimeMillis()-start)+"ms");
    }

    //CompletableFuture会使用一个默认的线程池，这个线程池是ForkJoinPool.commonPool()。
    static class RemoteService {
        public String timeoutGet(String key) throws RemoteException{
            // 定义一个CompletableFuture，用于处理getRemote的调用
            CompletableFuture<String> futureGet = CompletableFuture.supplyAsync(new Supplier<String>() {
                @Override
                public String get() {
                    return RemoteService.this.getRemote(key);
                }
            });


//            CompletableFuture<String> futureGet = CompletableFuture.completedFuture(getRemote(key));

            try {
                // 如果getRemote在1秒内返回，那么直接返回结果
                String result = futureGet.get(1, TimeUnit.SECONDS);
                if (RemoteExceptionTAG.equals(result)){
                    logger.info("timeoutGet: Exxxx");
                    throw new RemoteException(RemoteExceptionTAG+"-1");
                }
                return result;
            } catch (TimeoutException|InterruptedException|ExecutionException e) {
                // 如果getRemote在1秒内未返回，那么尝试调用getRemoteProvider三次
                for (int i = 0; i < 3; i++) {
                    try {
                        // 在每次调用getRemoteProvider时，如果在500毫秒内没有返回结果，就认为调用失败
                        String result = CompletableFuture.supplyAsync(new Supplier<String>() {
                                    @Override
                                    public String get() {
                                        return RemoteService.this.getRemoteProvider(key);
                                    }
                                })
                                .get(500, TimeUnit.MILLISECONDS);
                        if (result != null) {
                            // 如果getRemoteProvider有返回结果，那么返回该结果
                            return result;
                        }
                    } catch (Exception ex) {
                        // 如果getRemoteProvider在500毫秒内没有返回结果，那么跳过本次循环，进行下一次尝试
                        continue;
                    }
                }

                try {
                    // 如果getRemoteProvider都没有返回结果，那么等待getRemote最多3秒
                    return futureGet.get(1, TimeUnit.SECONDS);
                    //最后。
                } catch (Exception ex) {
                    // 如果getRemote在1秒内仍未返回，那么返回null
                    return null;
                }
            }
        }

        // 模拟RPC调用
        private String getRemote(String key) {
            // RPC调用
            logger.info("getRemote: key:"+key);
            try {
                //测试 Exception
//                if (1>0){
//                    try {
//                        throw new RemoteException("abcc");
//                    } catch (RemoteException e) {
//                        e.printStackTrace();
//                        return RemoteExceptionTAG;
//                    }
//                }

                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            logger.info("getRemote: over");
            return key+"value";
        }

        // 模拟RPC调用
        private String getRemoteProvider(String key) {
            logger.info("getRemoteProvider: key:"+key);
            // RPC调用
            return key+"ProviderValue";
        }
    }

}
